use core::time::Duration;
use regex::Regex;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
use std::vec;
use crate::file_iterator::RecursiveFileIterator;

/// 搜索到的目标结果
#[derive(Debug)]
struct SearchResult {
    // 行号
    line_number: usize,
    // 行内容
    line_content: String,
    // 引用的文件
    file: Arc<PathBuf>,
}

/// 从根目录，读取所有的文件，并将其写入到todo_queue队列中
pub fn search_target_files(todo_queue: Arc<Mutex<Vec<PathBuf>>>, root_dir: String) {
    let dir = Path::new(&root_dir).to_path_buf();
    let mut results = recurse(&dir);
    let mut x = todo_queue.lock().unwrap();
    x.append(&mut results);
}

/// 递归遍历，得到所有的文件
fn recurse(dir: &PathBuf) -> Vec<PathBuf> {
    let mut results = vec![];
    assert_eq!(dir.is_dir(), true);
    let children = std::fs::read_dir(dir).unwrap();
    let mut cursor = children.into_iter();
    while let Some(child) = cursor.next() {
        let child = child.unwrap();
        let child = child.path();
        if child.is_dir() {
            let mut res = recurse(&child);
            results.append(&mut res);
        } else {
            if is_text(&child) {
                results.push(child);
            }
        }
    }
    results
}

/// 判断文件是否是文本文件
fn is_text(path_buf: &PathBuf) -> bool {
    let suffixs = vec![
        // 偏向后端类编程语言
        ".java", ".py", ".go", ".rs", ".cpp", 
        // 偏向后端类编程语言
        ".html", ".htm", ".js", ".css", ".ts", ".vue", ".php", ".asp", ".jsp",
        // 配置文件类
        ".yml", ".yaml", ".conf", ".cfg", ".properties", ".toml", ".ini", ".xml",
        // 偏向数据类
        ".json", ".json5", ".txt", ".text", ".md", ".csv", ".log",
        // 操作系统相关
        ".sh", ".bat", ".cmd"
    ];
    for suf in suffixs {
        if path_buf.to_str().unwrap().ends_with(suf) {
            return true;
        }
    }
    false
}

fn content_match(
    input_thread_status: Arc<Mutex<bool>>,
    todo_queue: Arc<Mutex<Vec<PathBuf>>>,
    text: String,
    is_regex: bool,
    tx: mpsc::Sender<SearchResult>,
) {
    let re = match is_regex {
        true => Some(Regex::new(&text).expect("正则表达式错误，请检查...")),
        false => None,
    };

    loop {
        let target = todo_queue.lock().unwrap().pop();
        match target {
            Some(path_buf) => {
                do_retrieval(path_buf, &text, &re, &tx);
            }
            None => {
                if !*input_thread_status.lock().unwrap() {
                    break;
                } else {
                    thread::sleep(Duration::from_secs(2));
                }
            }
        }
    }
}

fn do_retrieval(
    path_buf: PathBuf,
    text: &str,
    re: &Option<Regex>,
    tx: &mpsc::Sender<SearchResult>,
) {
    let file = File::open(&path_buf).unwrap();
    let mut reader = BufReader::new(file);
    let path_buf2 = Arc::new(path_buf);

    // 使用字节缓冲区存储每行数据
    let mut buffer = Vec::new();
    let mut line_number = 0;

    // 直接使用reader.lines()会尝试将每行内容解码为 UTF-8 字符串，而文件中包含非 UTF-8 编码的字节会报错
    while reader.read_until(b'\n', &mut buffer).unwrap() > 0 {
        line_number += 1;
        // 处理行尾的换行符（兼容Windows和Unix换行格式）
        if buffer.ends_with(&[b'\r', b'\n']) {
            buffer.truncate(buffer.len() - 2);
        } else if buffer.ends_with(&[b'\n']) || buffer.ends_with(&[b'\r']) {
            buffer.truncate(buffer.len() - 1);
        }

        // 安全地将字节转换为字符串，无效UTF-8字符会被替换为�
        let line = String::from_utf8_lossy(&buffer);
        let line = line.to_string();
        buffer.clear();

        match re {
            Some(re) => {
                if re.is_match(&line) {
                    tx.send(SearchResult {
                        line_number: line_number,
                        line_content: line.to_string(),
                        file: Arc::clone(&path_buf2),
                    })
                    .unwrap();
                }
            }
            None => {
                if line.contains(text) {
                    tx.send(SearchResult {
                        line_number: line_number,
                        line_content: line.to_string(),
                        file: Arc::clone(&path_buf2),
                    })
                    .unwrap();
                }
            }
        }
    }
}

pub fn run(dir: &str, text: &str, is_regex: bool) {
    // 输入线程的存活状态
    let input_thread_status = Arc::new(Mutex::new(true));
    // 待搜索的文件队列
    let todo_queue: Arc<Mutex<Vec<PathBuf>>> = Arc::new(Mutex::new(Vec::new()));
    // 结果使用channel进行收集
    let (tx, rx) = mpsc::channel();

    // 启动一个新线程，搜索目标文件，然后将其放入到待检索的目标队列中
    let todo_queue_copy1 = Arc::clone(&todo_queue);
    let input_thread_status_copy1 = Arc::clone(&input_thread_status);

    let dir2 = dir.to_string();
    let _h1 = thread::spawn(move || {
        let file_iterator = RecursiveFileIterator::new(Path::new(&dir2)).unwrap();
        let mut results = vec![];
        for (i, file_path) in file_iterator.enumerate() { 
            results.push(file_path);
            if (i + 1) % 10 == 0 {
                let mut x = todo_queue_copy1.lock().unwrap();
                x.append(&mut results);
            }
        }
        if results.len() > 0 {
            let mut x = todo_queue_copy1.lock().unwrap();
            x.append(&mut results);
        }
        let mut status = input_thread_status_copy1.lock().unwrap();
        *status = false;
    });

    // 启动多个线程，对待检索的目标队列进行检索，并将检索的结果存放到 结果队列中
    let mut threads = vec![];
    for _ in 0..8 {
        let todo_queue_tmp = Arc::clone(&todo_queue);
        let input_thread_status_tmp = Arc::clone(&input_thread_status);

        let text2 = text.to_string();
        let tx_copy = tx.clone();
        let h = thread::spawn(move || {
            content_match(input_thread_status_tmp, todo_queue_tmp, text2, is_regex, tx_copy);
        });
        threads.push(h);
    }

    // 输入线程已完成的情况下，表示不会再有新数据进入到todo_queue队列中
    // h1.join().unwrap();
    // {
    //     let mut status = input_thread_status.lock().unwrap();
    //     *status = false;
    // }

    // 手动释放主线程的 tx，因为我们不需要再发送消息了
    drop(tx);
    let mut count = 0;
    for r in rx {
        println!("{}:{:<5} => {}", r.file.as_path().to_str().unwrap().replace("\\", "/"), r.line_number, r.line_content.trim());
        count += 1;
    }
    println!("共找到 {} 条结果", count);

    // 等待所有的线程执行完毕
    for h in threads {
        h.join().unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::BufRead;
    use std::io::BufReader;
    use std::path::Path;

    #[test]
    fn test_recurse() {
        let x = Path::new("d:/rust-workspace/text-search").to_path_buf();
        let results = recurse(&x);
        println!("{:#?}", results);
    }

    #[test]
    fn test_contains() {
        // let xx = "www.hbgrand.com";
        let x = String::from("www.hbgrand.com");
        println!("{}", x.contains("hbgrand.com"));
    }

    #[test]
    fn test02() {
        let x = Path::new("D:/git-repo-rust/text-search/docs/xxx.txt").to_path_buf();
        let re = Regex::new("hbgrand\\.com").ok();
        let (tx, rx) = mpsc::channel();
        do_retrieval(x, "hbgrand.com", &re, &tx);
        for received in rx {
            println!("Got: {:?}", received);
        }
    }

    #[test]
    fn test_file_content_read() {
        let path_buf = Path::new("D:/rust-workspace/text-search/README.md").to_path_buf();
        let file = File::open(&path_buf).unwrap();
        let mut reader = BufReader::new(file);
        let mut buffer = Vec::new();
        while reader.read_until(b'\n', &mut buffer).unwrap() > 0 {
            // 处理行尾的换行符（兼容Windows和Unix换行格式）
            if buffer.ends_with(&[b'\r', b'\n']) {
                buffer.truncate(buffer.len() - 2);
            } else if buffer.ends_with(&[b'\n']) || buffer.ends_with(&[b'\r']) {
                buffer.truncate(buffer.len() - 1);
            }

            // 安全地将字节转换为字符串，无效UTF-8字符会被替换为�
            let line = String::from_utf8_lossy(&buffer);
            let line = line.to_string();
            println!("{}", line);
            buffer.clear();
        }
    }
}
